/* 
 * @moduleName: DBUtils.js
 * @Desc: 这个模块考虑到页面有可能是个单独的新增或者编辑页面所以要把DBTable或者DBform的一些公用工具函数抽离出来。
 * 或者换个角度 直接把这些公共的工具函数 全部放到一个公共js文件来维护也是不错的选择 要不然项目文件太乱了
 * @Author: djkloop 
 * @Date: 2017-11-28 17:57:13 
 * @Last Modified by: djkloop
 * @Last Modified time: 2017-12-14 13:57:39
 */
import Logger from '../utils/Logger'
import globalConfig from '../config'
import http from '../utils/https'
import moment from 'moment'
const logger = Logger.getLogger('DBUtils')

// 这个工具函数需要存一些缓存
// DBLocalSchemaMap, key -> tableNmae, value -> {queryschmea, datashema}
const DBLocalSchemaMap = new Map()
// configMap, key -> tableName, value -> tableConfig
const configMap = new Map();

export default {
  /**
   * 通过tableName从缓存中取数据
   * 
   * @param {*} tableName 
   */
  getCacheSchema(tableName) {
    return DBLocalSchemaMap.get(tableName)
  },
  /**
   * http://www.ruanyifeng.com/blog/2015/05/async.html
   * async如果不知道怎么用或者是干什么的建议去看下
   * 我个人的理解就是为了解决回调地狱的问题。
   * 
   * @param {obj} props 
   * @param {func} callback 
   */
  async tryGetSchema(props, callback) {
    this.tableName = null;
    const {tableName} = props.match.params;
    this.tableName = tableName;
    if (tableName) {
      logger.debug('tryGetSchema init component DBTable with tableName = %s', tableName)
    } else {
      logger.error('tryGetSchema are you sure have schema?')
      return
    }
    // 拿到配置
    let tableConfig = this.getConfig(tableName)
    // 这里判断是为了给个提示 没有写配置信息 有些东西就显示不出来了，
    if (tableConfig === void 0) {
        logger.info(`tryGetSchema 没有配置相关操作...`, tableConfig)
    }
    let _cacheMap = this.getCacheSchema(tableName)
    if (!_cacheMap) {
      if (tableConfig.asyncSchema === true) {
        this.setState({loadingSchema: true})
        _cacheMap = await this.getRemoteSchema(tableName)
      } else {
        _cacheMap = this.getLocalSchema(tableName)
      }
    }
    const res = {..._cacheMap, tableName, tableConfig}
    callback(res)
  },
  /**
   * 
   * @param {obj} input 
   */
  updateState(input) {
    // 其实很多这种this.xxx变量也可以做成状态, 看情况了
    // 关键是这些变量变化时, 是否要触发重新render?
    // 这两项是肯定会有的
    let _tableName,
        _tableConfig,
        _dataSchema,
        _querySchema,
        _inited;

    _tableName = input.tableName;
    _tableConfig = input.tableConfig;

    if (input.dataSchema) {
      _dataSchema = input.dataSchema;
    } else {
      _inited = false;
      this.errorMsg = `加载${input.tableName}表的dataSchema出错, 请检查配置`;
      return;
    }

    if (input.querySchema) {
      _querySchema = input.querySchema;
    } else {
      _inited = false;
      this.errorMsg = `加载${input.tableName}表的querySchema出错, 请检查配置`;
      return;
    }
    _inited = true;
    const _resObj = {inited:_inited,querySchema:_querySchema,dataSchema:_dataSchema,tableConfig: _tableConfig,tableName:_tableName}

    // 如果一切正常, 设置init=true
    // 就是刷新下页面
    return _resObj
  },
  /**
   * 合并object更新参数
   * 
   * @param {Object} local 
   * @param {Object} remote 
   */
  merge(local, remote) {
    logger.info('merge ', local)
    if (local && remote) {
      const result = local
      const map = new Map()
      result.forEach(item => map.set(item.key, item));

      remote.forEach(v => {
        if (map.has(v.key)) {
          Object.assign(map.get(v.key), v)
        } else {
          result.push(v)
        }
      })
      return remote
    } else {
      return local || remote
    }
  },
  /**
   * 从本地获取schema
   * 
   * @param {Object} tableName 
   */
  getLocalSchema(tableName) {
    const ignoreCache = this.shouldIgnoreSchemaCache(tableName)
    let dataSchema,querySchema;
    try {
      dataSchema = require(`../DBLocalSchema/${tableName}.dataSchema.js`)
    } catch (error) {
      logger.error('load local data schema error: %o', error)
    }

    try {
      querySchema = require(`../DBLocalSchema/${tableName}.querySchema.js`)
    } catch (error) {
      logger.error('load local query schema error: %o', error)
    }
    const toCache = {dataSchema,querySchema}
    if (!ignoreCache) {
      DBLocalSchemaMap.set(tableName, toCache)
    }
    return toCache
  },
  /**
   * 从远程获取schema
   * 
   * @param {Object} tableName 
   */
  async getRemoteSchema(tableName) {
    
    const ignoreCache = this.shouldIgnoreSchemaCache(tableName)
    // 这里获取一下本地的文件是为了合并 如果没有则默认去拿服务器端配置
    const localSchema = this.getLocalSchema(tableName)
    let dataSchema,querySchema;
    try {
      const _HttpRes = await http.get(`../DBLocalSchema/${tableName}.dataSchema.js`)
      if (_HttpRes.data.code === 0) {
        dataSchema = this.merge(localSchema.dataSchema, _HttpRes.data.dataSchema)
      } else {
        this.error(`请求asyncSchema失败`)
      }
    } catch (error) {
      logger.error('getRemoteSchema response error: %o', error);
    }

    try {
      const _HttpRes = await http.get(`../DBLocalSchema/${tableName}.querySchema.js`)
      if (_HttpRes.data.code === 0) {
        querySchema = this.merge(localSchema.querySchema, _HttpRes.data.querySchema)
      } else {
        this.error(`请求asyncSchema失败`)
      }
    } catch (error) {
      logger.error('load local query schema error: %o', error)
    }
     // 更新缓存
    const toCache = {dataSchema,querySchema}
     if (!ignoreCache) {
       DBLocalSchemaMap.set(tableName, toCache);
     }
     return toCache;
  },
  /**
   * 获取到本地Config
   * 
   * @param {Object} tableName 
   */
  getConfig(tableName) {
    if(configMap.has(tableName)) {
      return configMap.get(tableName)
    }
    let tableConfig;
    try {
      // 我在这里理解的是获取到本地配置 因为配置什么的可以放在前台来做 当然后台想做的话 也可以照样发送一堆json也是可以做的
      // 或者可以在本地配置一些公共的 然后后台在请求过来一些
      // 如果想放在后台做的话  那就在这里放个ajax请求就OK了
      // http.get(`/api/getConfig/${tableName}/`) 我这个接口还没有写 后面再加吧
      const perConfig = require(`../DBLocalSchema/${tableName}.config`)
      // tableConfig = {...globalConfig.DBTable.default, perConfig}   --> 这种写法不适合这个场景 虽然效果也不错
      tableConfig = Object.assign({}, globalConfig.DBTable.default, perConfig)
    } catch (error) {
      logger.error('load local config error: %o', error)
    }
    // 设置下缓存然后就可以返回配置信息了...
    configMap.set(tableName, tableConfig)
    return tableConfig;
  },
  /**
   * 转化
   * 
   */
  transformRawTimeToObject(obj) {
    const newObj = {};
    for (const key in obj) {
      // rawData中可能有些undefined或null的字段, 过滤掉
      if (!obj[key])
        continue;
        if(Array.isArray(obj[key])) {
          obj[key].forEach((v,index) => {
            if(!moment.isMoment(v) && v.indexOf('-') !== -1) {
              obj[key][index] = moment(new Date(obj[key][index]),'YYYY-MM-DD HH:mm:ss', false)
            } else if(moment.isMoment(v)){
                v = moment(new Date(v),'YYYY-MM-DD HH:mm:ss', false)
            }
          })
        } else if(Object.prototype.toString.call(obj[key]) === "[object String]" && obj[key].indexOf('-') !== -1) {
          // 拿到时间进行格式化
          obj[key] = moment(new Date(obj[key]),'YYYY-MM-DD HH:mm:ss', false)
        } else {
        newObj[key] = obj[key];
      }
    }
    return obj;
  },
  /**
   * 把时间转化为object
   */
  transformRawDataTimeToObject(obj,filedMap) {
    const newObj = {};
    // 这段代码真是好蛋疼...
    for (const key in obj) {
      if (filedMap.get(key).$$optionMap) {
        const optionMap = filedMap.get(key).$$optionMap;
        if (obj[key] instanceof Array) {
          const newArray = [];
          for (const optionKey of obj[key]) {
            newArray.push(optionMap[optionKey]);
          }
          newObj[key] = newArray.join(',');
        } else {
          newObj[key] = optionMap[obj[key]];
        }
      } else {
        // 拿到时间进行格式化
        if(Array.isArray(obj[key])) {
          obj[key].forEach((v,index) => {
            if(!moment.isMoment(v) && v.indexOf('-') !== -1) {
              obj[key][index] = moment(new Date(obj[key][index]),'YYYY-MM-DD HH:mm:ss', false)
            } else if(moment.isMoment(v)){
                v = moment(new Date(v),'YYYY-MM-DD HH:mm:ss', false)
            }
          })
        } else if(Object.prototype.toString.call(obj[key]) === "[object String]" && obj[key].indexOf('-') !== -1) {
          // 拿到时间进行格式化
          obj[key] = moment(obj[key]).format('YYYY-MM-DD HH:mm:ss')
        } 
        newObj[key] = obj[key];
      }
    }
    newObj.$$rawData = obj;  // 原始数据还是要保存下的, 后面update会用到
    return newObj;
  },

  /**
   * 把时间转化为字符串
   * @param {*} obj 
   */
  transformRawDataTimeToString(obj, filedMap) {
    debugger
    const newObj = {};
    // 这段代码真是好蛋疼...
    for (const key in obj) {
      if (filedMap.get(key).$$optionMap) {
        const optionMap = filedMap.get(key).$$optionMap;
        if (obj[key] instanceof Array) {
          const newArray = [];
          for (const optionKey of obj[key]) {
            newArray.push(optionMap[optionKey]);
          }
          newObj[key] = newArray.join(',');
        } else {
          newObj[key] = optionMap[obj[key]];
        }
      } else {
        // 拿到时间进行格式化
        if(Array.isArray(obj[key])) {
          obj[key].forEach((v,index) => {
            if(!moment.isMoment(v) && v.indexOf('-') !== -1) {
              obj[key][index] = moment(obj[key][index]).format('YYYY-MM-DD HH:mm:ss')
            } else if(moment.isMoment(v)){
              obj[key][index] = moment(obj[key][index]).format('YYYY-MM-DD HH:mm:ss')
            }
          })
        } else if(moment.isMoment(obj[key])) {
          // 拿到时间进行格式化
          obj[key] = moment(obj[key]).format('YYYY-MM-DD HH:mm:ss')
        } 
        newObj[key] = obj[key];
      }
    }
    return newObj
  },
  /**
   * 是否忽略缓存
   * 
   * @param {*} tableName 
   */
  shouldIgnoreSchemaCache(tableName) {
    const tableConfig = this.getConfig(tableName)
    return tableConfig.asyncSchema === true && tableConfig.ignoreSchemaCache === true
  }
} 